package com.settlement.system.service.extend;

import com.settlement.system.common.exception.ServiceException;
import com.settlement.system.common.util.common.AcsThreadPoolUtils;
import com.settlement.system.service.IBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 多线程批量新增,不保证事务
 *
 * @author Ximi
 * @since 2020/09/21
 */
@Slf4j
public class UnitPriceBatchSave<T> extends BatchSave<T> {

    public static ExecutorService executorService = AcsThreadPoolUtils.getExecutorService();

    private AtomicInteger duplicateNumber = new AtomicInteger(0);

    public void setDuplicateNumber(AtomicInteger duplicateNumber) {
        this.duplicateNumber = duplicateNumber;
    }

    public UnitPriceBatchSave(IBaseService baseService, List<T> datas) {
        super(baseService,datas);
    }

    @Override
    public void save() throws ServiceException {
        List<Future<Void>> futures = new ArrayList<>();
        for (int index = 0; index < datas.size(); index += periodNumber) {
            int rightIndex = index + periodNumber - 1;
            rightIndex = rightIndex > datas.size() - 1 ? datas.size() - 1 : rightIndex;
            SaveTask saveTask = new SaveTask(index, rightIndex);
            Future<Void> future = executorService.submit(saveTask);
            futures.add(future);
        }

        for (Future<Void> future : futures) {
            try {
                future.get();
            } catch (Exception ex) {
                ex.printStackTrace();
                log.error("执行异常", ex);
                exception = ex;
            }
        }
    }

    private class SaveTask implements Callable<Void> {

        public Integer leftIndex;
        public Integer rightIndex;

        public SaveTask(Integer leftIndex, Integer rightIndex) {
            this.leftIndex = leftIndex;
            this.rightIndex = rightIndex;
        }

        @Override
        public Void call() {
            List<Entity> waitSaveDatas = new ArrayList<>();
                for (int index = leftIndex; index <= rightIndex; index++) {
                    waitSaveDatas.add(datas.get(index));
                    if (waitSaveDatas.size() == 50 || index == rightIndex) {
                        try {
                            baseService.save(getDatas(waitSaveDatas));
                        } catch (Exception ex) {
                            ex.printStackTrace();
                            if (ex instanceof DuplicateKeyException) {
                                try {
                                    findDuplicateKeyException(waitSaveDatas);
                                } catch (ServiceException e) {
                                    exception = ex;
                                    errorMessage = "系统异常";
                                }
                            } else {
                                exception = ex;
                                errorMessage = "系统异常";
                            }
                        }
                        waitSaveDatas.clear();
                    }
                }
            return null;
        }
    }

    @Override
    public String getErrorMessage() {
        return errorMessage;
    }

    /**
     * 寻找异常
     *
     * @param list
     */
    private void findDuplicateKeyException(List<Entity> list) throws ServiceException {
        for (Entity entity : list) {
            try {
                baseService.insert(entity.getData());
            } catch (Exception ex) {
                ex.printStackTrace();
                if (ex instanceof DuplicateKeyException) {
                    duplicateNumber.incrementAndGet();
                } else {
                    throw new ServiceException("第" + entity.getOrder() + "数据导入异常",ex);
                }
            }
        }
    }

    public AtomicInteger getDuplicateNumber() {
        return duplicateNumber;
    }
}
